package edu.hawaii.halealohacli;

import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Scanner;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.sensordata.jaxb.SensorData;

/**
 * Monitor-goal class that gets the current power based on a tower or lounge name at a given
 * interval and compares it to a goal power consumption.
 * 
 * @author John MacMillan
 * @author Mitchell Kupfer
 * @author Bryson Hori
 * @author Chris Fang
 * @author Daniel Ly
 */
public class MonitorGoal {
  protected String tower;
  WattDepotClient client;
  protected int interval;
  protected int goal;
  protected Baselist baseList;

  /**
   * Constructor method.
   * 
   * @param tower the tower we want to get the current power for.
   * @param client the WattDepot client.
   * @param goal the percentage to take off from the baseline value.
   * @param interval the interval at which to continuously get the current power.
   * @param baseList the hashmap to access baseline values.
   */
  public MonitorGoal(String tower, WattDepotClient client, int goal, int interval,
      Baselist baseList) {
    this.tower = tower;
    this.client = client;
    this.goal = goal;
    this.interval = interval;
    this.baseList = baseList;
  }

  /**
   * Simple accessor method to get the value of tower of the tower name.
   * 
   * @return Returns the value of tower.
   */
  public String getTower() {
    return this.tower;
  }
  
  /**
   * Runs the actual command.
   * 
   * @throws Exception If problem occurs when running through data from server.
   * @return a boolean, true if command is successful.
   */
  public boolean run() throws Exception {
    if (baseList.contains(tower)) {
      System.out.format("(Press the return key to stop monitoring goal)%n%n");
      PrinterThread2 printer =
          new PrinterThread2(tower, client, goal, interval, baseList.get(tower));
      printer.start();
      while (System.in.available() == 0) {
        Thread.sleep(1); // brief pause
      }
      printer.exit();
      printer.interrupt();
      Scanner eraser = new Scanner(System.in);
      eraser.nextLine();

      return true;
    }
    else {
      System.out.println("Error - Baseline for tower may not have been set");
      return false;
    }
  }
}

/**
 * An interruptible thread which prints the current power until it is told to exit.
 * 
 * @author Chris Fang
 * @author Daniel Ly
 */
class PrinterThread2 extends Thread {

  int interval;
  Date date;
  double powerConsumed;
  SensorData latestData;
  Format format;
  String tower;
  WattDepotClient client;
  int goal;
  double[] baselines;
  boolean exit;

  /**
   * Constructor for the thread in order to communicate with WattDepot and repeatedly print out the
   * results at the given interval.
   * 
   * @param tower the tower we want to get the current power for.
   * @param client the WattDepot client.
   * @param goal the percentage to take off from the baseline value.
   * @param interval the interval at which to continuously get the current power.
   * @param baselines the 24 baseline values for each hour.
   */
  protected PrinterThread2(String tower, WattDepotClient client, int goal, int interval,
      double[] baselines) {
    this.tower = tower;
    this.client = client;
    this.goal = goal;
    this.interval = interval;
    this.baselines = baselines;
    this.format = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss", Locale.US);
    this.exit = false;
  }

  /**
   * Execution call for the thread.
   */
  public void run() {
    while (!exit) {
      try {
        print();
        sleep();
      }
      catch (Exception e) {
        System.out.println("An unexpected error has occurred");
      }
    }
  }

  /**
   * Sets the exit flag to true.
   */
  public void exit() {
    this.exit = true;
  }

  /**
   * Prints out whether the latest available power reading of the given tower is under the set
   * baseline.
   * 
   * @throws Exception if anything goes wrong with WattDepot interaction
   * @return boolean true if no exception, false otherwise.
   */
  protected boolean print() throws Exception {
    try {
      double powerGoal;
      Double baseline;
      String result;
      latestData = this.client.getLatestSensorData(this.tower);
      // get power consumed based on the latest sensor data.
      powerConsumed = client.getPowerConsumed(this.tower, latestData.getTimestamp());
      // converts the timestamp from latest sensor data to a date object.
      date = latestData.getTimestamp().toGregorianCalendar().getTime();
  
      baseline = baselines[latestData.getTimestamp().getHour()];
      // prints out the found corresponding data to the tower name.
      System.out.format("%s's power as of %s was %s kW%n", this.tower, format.format(date),
          powerConsumed / 1000);
      powerGoal = ((100 - this.goal) * 0.01) * (baseline);
      if (powerConsumed < powerGoal) {
        result = "The tower/lounge is meeting its power conservation goal.";
      }
      else {
        result = "The tower/lounge is not meeting its power conservation goal.";
      }
      System.out.format("%s%n", result);
      return true;
    }
    catch (MiscClientException e) {
      return false;
    }
  }

  /**
   * Puts the thread to sleep for the specified interval.
   */
  private void sleep() {
    try {
      Thread.sleep(this.interval * 1000);
    }
    catch (InterruptedException e) {
      // do nothing
    }
  }
}